#include <io_milc_v5.h>

#define WQUARK_BYTES_PER_SITE (24 * sizeof(float))

int MILC_v5_read_gauge_header ( IO_filehand* fh,
				MILC_v5_gauge_header* header )
{
  int ret, status = 0;
  int len = sizeof(MILC_v5_gauge_header);

  ret = fread ( static_cast ( void*, header ), sizeof ( char ),
		len, fh -> data_fp );
  if ( ret != len )
    {
      status += 1;
      fprintf ( stderr, "WARNING: header read incomplete.\n" );
    }

  if ( header -> magic_number != MILC_V5_GAUGE )
    {
      int cnt;

      /* try byte swapping */
      IO_swap32 ( &(header -> magic_number) );

      for ( cnt = 0; cnt < 4; ++cnt )
	IO_swap32 ( &(header -> dim[cnt]) );

      /* timestamp (char) needs no swapping */

      IO_swap32 ( &(header -> site_order) );

      if ( header -> magic_number != MILC_V5_GAUGE )
	{
	  fprintf ( stderr, "WARNING: read unknown header.\n" );
	  status += 2;
	}
      else
	{
	  /* this file needs byte swapping */
	  fh -> swap_bytes = 1;
	}
    }

  return status;
}

int MILC_read_checksum ( IO_filehand* fh, MILC_check32* check )
{
  int ret, status = 0;

  ret = fread ( check, sizeof(IO_type32u), 2, fh -> data_fp );
  if ( ret != 2 )
    status += 1;

  if ( fh -> swap_bytes )
    {
      IO_swap32 ( static_cast ( IO_type32*, check ) + 0 );
      IO_swap32 ( static_cast ( IO_type32*, check ) + 1 );
    }

  return status;
}

void MILC_v5_print_gauge_header ( FILE *out, MILC_v5_gauge_header* header )
{
  fprintf ( out, "MILC v5 gauge file\n" );
  fprintf ( out, "header.magic-number 0x%08x\n", header -> magic_number );
  fprintf ( out, "header.dim[0] %d\n", header -> dim[0] );
  fprintf ( out, "header.dim[1] %d\n", header -> dim[1] );
  fprintf ( out, "header.dim[2] %d\n", header -> dim[2] );
  fprintf ( out, "header.dim[3] %d\n", header -> dim[3] );
  fprintf ( out, "header.timestamp %s\n", header -> timestamp );
  fprintf ( out, "header.site-order %d\n", header -> site_order );
}

/* can only read 32-bit types */
/* nelem is number of 32-bit elements to be read */
int MILC_read_site_data32 ( IO_filehand* fh, int nelem, void* site_data )
{
  int ret, status = 0;

  /* 32-bit only: float, int */
  ret = fread ( site_data, sizeof(IO_type32), nelem, fh -> data_fp );
  if ( ret != nelem )
    status += 1;

  if ( fh -> swap_bytes )
    {
      int cnt;
      for ( cnt = 0; cnt < nelem; ++cnt )
	IO_swap32 ( static_cast ( IO_type32*, site_data ) + cnt );
    }

  return status;
}

int MILC_v5_read_wquark_file_header ( IO_filehand* fh,
				      MILC_v5_wquark_header* header )
{
  int ret, status = 0;
  int len = sizeof(MILC_v5_wquark_header);

  ret = fread ( static_cast ( void*, header ), sizeof ( char ),
		len, fh -> data_fp );
  if ( ret != len )
    {
      status += 1;
      fprintf ( stderr, "WARNING: header read incomplete.\n" );
    }

  if ( header -> magic_number != MILC_V5_W_PROP )
    {
      int cnt;

      /* try byte swapping */
      IO_swap32 ( &(header -> magic_number) );

      for ( cnt = 0; cnt < 4; ++cnt )
	IO_swap32 ( &(header -> dim[cnt]) );

      /* timestamp (char) needs no swapping */

      IO_swap32 ( &(header -> site_order) );

      IO_swap32 ( &(header -> n_src_spins) );

      for ( cnt = 0; cnt < MAX_SOURCE_SPINS; ++cnt )
	IO_swap32 ( &(header -> src_spin_list[cnt]) );

      if ( header -> magic_number != MILC_V5_W_PROP )
	{
	  fprintf ( stderr, "WARNING: read unknown header.\n" );
	  status += 2;
	}
      else
	{
	  /* this file needs byte swapping */
	  fh -> swap_bytes = 1;
	}
    }

  return status;
}

void MILC_v5_print_wquark_file_header ( FILE* out,
					MILC_v5_wquark_header* header )
{
  int cnt;

  fprintf ( out, "MILC v5 Wilson quark file\n" );
  fprintf ( out, "header.magic-number 0x%08x\n", header -> magic_number );
  fprintf ( out, "header.dim[0] %d\n", header -> dim[0] );
  fprintf ( out, "header.dim[1] %d\n", header -> dim[1] );
  fprintf ( out, "header.dim[2] %d\n", header -> dim[2] );
  fprintf ( out, "header.dim[3] %d\n", header -> dim[3] );
  fprintf ( out, "header.timestamp %s\n", header -> timestamp );
  fprintf ( out, "header.site-order %d\n", header -> site_order );
  fprintf ( out, "header.nspins %d\n",  header -> n_src_spins );
  fprintf ( out, "header.spins" );
  for ( cnt = 0;
	cnt < header -> n_src_spins && cnt < MAX_SOURCE_SPINS; ++cnt )
    {
      fprintf ( out, " %d", header -> src_spin_list[cnt] );
    }
  fprintf ( out, "\n" );
}

int MILC_v5_read_wquark_rec_header ( IO_filehand* fh,
				     MILC_v5_wquark_rec_header* header )
{
  int cnt, ret, status = 0;
  int len = sizeof(MILC_v5_wquark_rec_header);

  ret = fread ( static_cast ( void*, header ), sizeof ( char ),
		len, fh -> data_fp );
  if ( ret != len )
    {
      status += 1;
      fprintf ( stderr, "WARNING: record header read is incomplete.\n" );
    }

  if ( fh -> swap_bytes )
    for ( cnt = 0; cnt < len/sizeof(IO_type32); ++cnt )
      IO_swap32 ( static_cast ( IO_type32*, header ) + cnt );

  return ( status );
}

void MILC_v5_print_wquark_rec_header ( FILE* out,
				       MILC_v5_wquark_rec_header* header )
{
  fprintf ( out, "record.spin  %d\n", header -> spin );
  fprintf ( out, "record.color %d\n", header -> color );
  fprintf ( out, "record.MILC-checksums 0x%08x 0x%08x\n",
	    header -> check.sum29,
	    header -> check.sum31 );
}

void MILC_v5_print_wquark_dir ( FILE* out, MILC_v5_wquark_directory* dir )
{
  int spin, color;
  int nspin = dir -> n_spin;

  fprintf ( out, "Wquark directory:\n" );
  for ( spin = 0; spin < nspin; ++spin )
    for ( color = 0; color < 3; ++color )
      MILC_v5_print_wquark_rec_header ( out, &(dir -> header[spin][color]) );
}

/*
** Seek and read record headers. Leaves file positioned ??
*/
int MILC_v5_build_wquark_dir ( IO_filehand* fh,
			       MILC_v5_wquark_header* fheader,
			       MILC_v5_wquark_directory* dir )
{
  int ret, spin, color;
  off_t foffset;
  int status = 0;
  int nspin = fheader -> n_src_spins;
  unsigned long recsz = sizeof(MILC_v5_wquark_rec_header) + WQUARK_BYTES_PER_SITE *
    fheader -> dim[0] *
    fheader -> dim[1] *
    fheader -> dim[2] *
    fheader -> dim[3];

  dir -> n_spin = nspin;

  /* skipping file header */
  foffset = sizeof(MILC_v5_wquark_header);
  if ( fheader -> site_order )
    {
      fprintf ( stderr,
		"Don't know how to handle non-standard site orders\n" );
      status += 1;
    }

  for ( spin = 0; spin < nspin; ++spin )
    for ( color = 0; color < 3; ++color )
      {
	if ( ( fseeko64 ( fh -> data_fp, foffset, SEEK_SET ) ) )
	  {
	    status += 2;
	    fprintf ( stderr, "Cannot seek file\n" );
	  }

	ret =  MILC_v5_read_wquark_rec_header ( fh,
						&(dir -> header[spin][color]) );

	/* skipping data record */
	foffset += recsz;
      }

  return ( status );
}

/*
** Position wquark file at start of data with indices
** src_spin_cnt, src_color_cnt, t.
*/
int MILC_v5_seek_wquark ( IO_filehand* fh, MILC_v5_wquark_header* fheader,
			  int src_spin_cnt, int src_color_cnt, int t )
{
  int status = 0;
  off_t foffset;
  long rheadsz = sizeof(MILC_v5_wquark_rec_header);
  unsigned long data3sz = WQUARK_BYTES_PER_SITE *
    fheader -> dim[0] *
    fheader -> dim[1] *
    fheader -> dim[2];

  /* skipping file header */
  foffset = sizeof(MILC_v5_wquark_header);
  if ( fheader -> site_order )
    {
      fprintf ( stderr,
		"Don't know how to handle non-standard site orders\n" );
      status += 1;
    }

  /* skipping record headers */
  foffset += rheadsz * ( 1 + src_color_cnt + 3 * src_spin_cnt );

  /* skipping 3-volume data blocks */
  foffset += data3sz * ( t +
			fheader -> dim[3] *
			( src_color_cnt + 3 * src_spin_cnt ) );

  if ( ( fseeko64 ( fh -> data_fp, foffset, SEEK_SET ) ) )
    {
      status += 2;
      fprintf ( stderr,
		"Cannot seek file\n" );
    }

  return status;
}
